(function (window) {
    // 节流函数
    window.throttle = function (fn, delay, immediately = false) {
        let preTime = Date.now();
        return function () {
            const context = this;
            const args = arguments;
            const nowTime = Date.now();
            if(immediately) {
                fn.apply(context, args);
                immediately = false;
            } else if(nowTime - preTime > delay) {
                fn.apply(context, args);
                preTime = Date.now();
            }
        }
    }

    // 防止自动锁屏 click, keydown, mousewheel事件更新自动锁屏信息
    document.addEventListener("click", throttle(updateLock, 500, True), True);
    document.addEventListener("keydown", throttle(updateLock, 500, true), true);
    document.addEventListener("mousewheel", throttle(updateLock, 500, true), true);

    // 更新锁屏定时器
    function updateLock() {
        localStorage.setItem("lockTimeSteamp", Date.parse(new Date()));
    };

    // 获取topWindow
    function getTopWindow(win) {
        if(!win) win = window;
        if(win.isJkTopWindow) return win;
        var pWindow = win.parent;
        if(pWindow === win) {
            return win;
        }
        return getTopWindow(pWindow);
    }
    var topWindow = getTopWindow();

    function writeErrorWebLog(type, moduleId, content, url = '', pathname = '') {
        if(typeof window.jk_ClientWeblog === "function") {
            try {
                window.jk_ClientWeblog(type, moduleId, content, url, pathname);
            } catch(e) {
                console.warn("写入weblog错误");
            }
        }
    };

    const errorStack = new Set();

    const inspectLogInWeb = (subType, content, extraData) => {
        // web版数据上报
		var login={"group_id":"g003","member_id":"547517112768259072","member_name":"800901","user_id":"2148020328425490432","user_name":"ZXY079","company":"新疆乐乐妈","mobile":"18340070905","realName":"杨飞","imgUrl":null,"isAdmin":0,"authFlag":0,"roles":"505","officialMember":1,"firstLogin":false,"tags":["web_and_service_gray_upgrade","fin_full_version","print_error_notify"],"apiHost":"https://g003-api.jackyun.com","spaceHost":"https://g003-slb.jackyun.com","host":"https://g003.jkyservice.com","hostIp":"http://47.92.44.184","grayHost":"https://env1.jkyservice.com","upgradeHost":"http://upgrade.jackyun.com","isGray":"0","pdaUpgradeHost":"upgrade.jackyun.com","webSocket":"wss://ws.jackyun.com/ws","grayWebSocket":"wss://ws.jackyun.com/ws00000","jdHost":"https://jd.jackyun.com","networkIp":"49.118.180.74","pddHost":"https://pdd.jakcyun.com","version":"v5","environment":"prod","softVersion":"standard","platCode":"JACKYUN","type":"1","riskType":null,"verifyId":null,"verifyType":null,"codeNotifyMobile":null,"codeNotifyEmail":null,"loginDevice":"web_browser","forceChangePassword":"0","isBindWeixinMiniProgram":"0","jdRiskId":null,"jdRiskUrl":null,"clientSn":"RFWIM56G-SFLR-BROSWER","userStatusMsg":null,"deviceName":"web_browser","assistMenuJson":null,"isAssistLogin":null,"groupUpgradeInfo":{"upgradeFileOssDir":"prod/resources/prod_g003/","resUpdateFileOssPath":"https://jkyun-client.oss-cn-hangzhou.aliyuncs.com/prod/resources/prod_g003/resupdate.ini?OSSAccessKeyId=LTAI5tEPrnVjgwhJiNno8cWH&Expires=3600000001698129400&Signature=0p%2BFiAUs3yEppPyIf1I%2BqrGe5c8%3D"},"memberStatus":3,"customDesignInfo":{"customDesignType":"0"},"isGreyTestMember":null,"greyTestWebPath":null,"access_token":"273B6959AC581D52D18933B207E9E705","refresh_token":"208082ADE4B21CCC4D96080D4BB15C8D","expires_in":"7199","scope":"all","jti":"abc0c586-dfde-48c8-a643-79044928e7a6","fastLoginToken":null,"onlinePlatCode":"TB_24869601"};
			
        const {
            user_name: userName,
            member_name: memberName,
        } = login;
        const {
            networkIp: hostIp,
            clientSn: clientId,
            deviceName: device
        } = topWindow.loginResult;
        const errorData = {
            subType,
            data: {
                timestamp: Date.now(),
                userName,
                memberName,
                hostIp,
                content: 'base64:' + topWindow.Base64.encode(content),
                extraData: 'base64:' + topWindow.Base64.encode(extraData),
                clientId,
                terminal: '',
                device
            }
        }
        errorStack.add(errorData);
        if(errorStack.size < 20) return;
        sendBeaconData();
    }

    const sendBeaconData = () => {
        const recordString = JSON.stringify([...errorStack, ...topWindow.jkUtils.errorWebLog.errorStack]);
        const recordData = {
            record: recordString
        };
        const signData = jkUtils.jkGetSign(recordData, {}, false);
        const data = new FormData();
        Reflect.ownKeys(signData).forEach(key => {
            data.append(key, signData[key]);
        });
        const sendResult = navigator.sendBeacon('/jkyun/basic-function/qualityMonitor/record', data);
        if(sendResult) {
            errorStack.clear();
            topWindow.jkUtils.errorWebLog.errorStack.clear();
            return;
        };
        // sendBeacon没有发送成功的话使用ajax请求
        jkUtils.jkAjax({
            url: "/jkyun/basic-function/qualityMonitor/record",
            type: "post",
            data: recordData
        }).done(() => {
            errorStack.clear();
            topWindow.jkUtils.errorWebLog.errorStack.clear();
        });
    }

    function inspectWeblog(subType, content, extraData) {
        try {
            const fileName = JSON.parse(extraData).fileName;
            if(fileName && fileName.indexOf('chrome-extension') !== -1) return;
        } catch(error) { }
        if(typeof window.jk_InspectWeblog === "function") {
            try {
                window.jk_InspectWeblog(subType, 'base64:' + topWindow.Base64.encode(content), 'base64:' + topWindow.Base64.encode(extraData));
            } catch(e) {
                console.warn("写入InspectWeblog错误");
            }
            return;
        }
        // web版数据上报
        inspectLogInWeb(subType, content, extraData);
    }

    function getErrorModuleInfo(prop) {
        try {
            var moduleInfo = jkUtils.jkGetModuleInfo();
            if(!moduleInfo) return "unknown";
            if(moduleInfo.moduleId === "mainframe") return "mainframe";
        } catch(e) {
            return "unknown";
        }
        return moduleInfo[prop];
    };

    /**
     *吉客云前端错误监控
     *error事件监听语法, 词法, 资源加载错误
     *资源加载错误无法获得详细信息, 只提示错误
     */
    window.addEventListener("error", writeErrorLog, true);
    // window.addEventListener('unhandledrejection', (e) => {
    //     // 抛出错误，统一让error事件处理
    //     throw e.reason;
    // });
    window.addEventListener('visibilitychange', () => {
        if(document.visibilityState === 'hidden' && typeof window.jk_InspectWeblog !== "function") {
            if(!errorStack.size && !topWindow.jkUtils.errorWebLog.errorStack.size) return;
            sendBeaconData();
        }
    })

    function writeErrorLog(e) {
        if(e.error) {
            setJavaScriptErrorLog(e);
        } else {
            setSourceErrorLog(e);
        }
    }

    let sourceErrorList = [];
    //记录资源加载错误
    function setSourceErrorLog(e) {
        var fileURL = false,
            subType = '0';
        //判断资源是script和link两种情况
        if(e.target.localName === "script") {
            fileURL = e.target.src;
        } else if(e.target.localName === "link") {
            fileURL = e.target.href;
        }
        sourceErrorList.push(fileURL);
        if(fileURL) {
            const index = fileURL.indexOf('?')
            if(index !== -1) {
                fileURL = fileURL.slice(0, index)
            }
            var pathname = window.location.pathname;
            var logContent = {
                resource: fileURL,
                pathname: pathname,
                moduleName: getErrorModuleInfo("moduleId")
            };

            var error = {
                resource: fileURL,
                status: '',
                timeout: ''
            }

            var logContentText = JSON.stringify(logContent);
            var errorText = JSON.stringify(error);
            var moduleId = getErrorModuleInfo("moduleId");
            inspectWeblog(subType, logContentText, errorText);
            writeErrorWebLog('1', moduleId, logContentText, fileURL, pathname);
            //如果检测到两个核心的CSS文件发生错误, 则进行提示, 并删除缓存, 避免长时间的界面错乱
            if(fileURL.indexOf("mainframe.css") > -1 || fileURL.indexOf("jkall.css") > -1) {
                alert("检测到核心资源文件加载错误, 点击确定将自动修复");
                window.jk_cleancache(1);
            }
        }
    }
    //记录代码运行时暴露的错误
    function setJavaScriptErrorLog(e) {
        //当页面发生代码错误时进行一次记录
        window.javaScriptErrorHappened = true;
        var pathname = window.location.pathname;
        var errorMsg = e.error.name + ':' + e.error.message;
        var logContent = {
            stack: e.error.stack,
            filename: e.filename,
            lineno: e.lineno,
            colno: e.colno,
            pathname: pathname,
            moduleName: getErrorModuleInfo("moduleName")
        };
        //错误的唯一标识
        var errorId = `${e.error.message};[${e.lineno},${e.colno}]`.replace(/\'|\"/g, '');
        var moduleId = getErrorModuleInfo("moduleName");
        //日志内容content
        var logContentText = JSON.stringify(logContent);
        var extra = {
            fileName: e.filename.split('?')[0],
            linkFile: pathname,
            moduleId: moduleId,
            errorId: errorId
        };
        var extraStr = JSON.stringify(extra);
        //保证字符串长度不超过500
        if(logContent.length >= 500) logContentText = logContentText.substring(0, 2047);
        var moduleId = getErrorModuleInfo("moduleId");
        writeErrorWebLog('2', moduleId, logContentText, errorMsg, pathname);
        inspectWeblog('1', logContentText, extraStr);
    }


    /** 
     * 2021年6月17日更新: 关于资源请求加载有问题的解决
     * 利用performance API中可以获取到资源加载的问题
     * 原理大致如下: 到达指定阈值(13s)之后, 通过选取标签元素(script和link)
     * 去和performance API中的各resource进行比较, 记录两种情况:
     * 1.花费时间太长的(3s阈值)
     * 2.resource中无法找到的标签元素内容(说明到达指定阈值之后元素还没有正确返回)
     * 低版本XP的壳内NodeList没有部署forEach方法, 先使用Array.from转化为数组
     * 2021年7月9日更新: 在Chrome中, 如果是动态插入的script标签, 并且是插入在head标签内部,
     * 并且是从命中本地缓存的情况下, performance API中并不会记录该条数据(已知问题)
     */

    setTimeout(() => {
        //阈值到达以后开始检测未完成的资源(css, js)
        let linkNodeList = Array.from(document.querySelectorAll("link"));
        let scriptNodeList = Array.from(document.querySelectorAll("script"));
        let sourceMap = new Map();
        linkNodeList.forEach(function (item) {
            !sourceErrorList.includes(item.href) && sourceMap.set(item.href, true);
        });
        scriptNodeList.forEach(function (item) {
            !sourceErrorList.includes(item.src) && sourceMap.set(item.src, true);
        })

        //单个资源加载时间超过3s的统一都要记录下来
        if(performance) {
            let performanceList = performance.getEntriesByType("resource").filter(function (item) {
                if(item.initiatorType === "script" || item.initiatorType === "link") {
                    return true;
                }
            });
            performanceList.forEach(function (item) {
                if(item.duration >= 3000) {
                    sourceMap.set(item.name, item.duration);
                } else if(sourceMap.has(item.name)) {
                    sourceMap.delete(item.name);
                }
            });

            sourceMap.forEach(function (value, key) {
                if(key) {
                    var subType = '0';
                    var pathname = window.location.pathname;
                    var logContent = {
                        resource: key,
                        pathname: pathname,
                        moduleName: getErrorModuleInfo("moduleId")
                    };
                    var error = {
                        resource: key,
                        status: '',
                        timeout: '',
                        duration: ''
                    }
                    //如果value还是true, 说明是没收到返回值的那一个, 标记为超时
                    if(value === true) {
                        error.timeout = 1;
                        logContent.timeout = 1;
                    } else {
                        error.duration = value.toFixed(2);
                        logContent.duration = error.duration;
                    }
                    var logContentText = JSON.stringify(logContent);
                    var errorText = JSON.stringify(error);
                    inspectWeblog(subType, logContentText, errorText);
                }
            });
        };
    }, 13000);
})(window);